Spectrum Virtualize RESTful API
Spectrum Virtualize Representational State Transfer (REST) モデルのアプリケーション・プログラミング・インターフェース (API) は、システム情報の取得と、システム・リソースの作成、変更、および削除に使用されるコマンド・ターゲットで構成されています。これらのコマンド・ターゲットにより、コマンド・パラメーターは、編集せずに Spectrum Virtualize コマンド・ライン・インターフェースにパススルーできるようになります。このコマンド・ライン・インターフェースでは、パラメーター指定の構文解析を処理して、妥当性検査およびエラー・レポートを行います。Hypertext Transfer Protocol Secure (HTTPS) を使用して、RESTful API サーバーと正常に通信します。
RESTful API サーバーでは、トランスポート・セキュリティー (SSL など) を考慮しませんが、要求がローカルの保護されたサーバーから開始されるものと想定しています。HTTPS プロトコルは、データ暗号化によってプライバシーを提供します。RESTful API はコマンド認証を要求することにより追加のセキュリティーを提供します。追加のセキュリティーは、2 時間のアクティブ状態または 30 分間の非アクティブ状態 (どちらか先に発生した方) の間、継続します。
Uniform Resource Locator (URL) は、システム上の異なるノード・オブジェクトをターゲットとします。HTTPS POST メソッドは、URL に指定されたコマンド・ターゲットに作用します。詳しくは、RESTful API コマンド・ターゲットと特性を参照してください。 システム上のさまざまなオブジェクトに関する情報を変更または表示するには、要求を作成してシステムに送信する必要があります。 次のセクションで説明するように、RESTful API サーバーが要求を受信してコマンドに変換するために特定のエレメントを提供する必要があります。
HTTPS 要求の作成
https://system_node_ip:7443/rest/command
ここで、すべてのコマンド (LS コマンドを含む) は、少なくとも 1 つの名前付きパラメーターを受け入れます。Spectrum Virtualize RESTful API は POST メソッドのみであるため、URI に定位置パラメーターを付加し、名前付きパラメーターを JavaScript Object Notation (JSON) ストリングとして要求本体に入れるという規則を実施します。
このコマンド・ターゲットをより簡潔に使用する方法は、POST を排他的に採用することでした。
前述のように、URL およびコマンド・ターゲットの名前に加え、その他の情報が、要求行と、指定されたオブジェクトに対して実行するアクションに関する HTTP 要求の本体で必要です。要求行には、POST HTTP メソッドを指定します。要求の本体に、必須パラメーター (RAID レベルや IP アドレスなど) を指定します。
{'X-Auth-Username': 'superuser'}HTTP/1.1 200 OK
Server: lighttpd/1.4.31
Date: date
Content-type: application/json; charset=UTF-8
Content-length: content_length
Connection: close
{"attribute": "value"}
API コマンド・ターゲットおよびそれらの特性を確認するには、RESTful API コマンド・ターゲットと特性を参照してください。開始方法の例については、始めにを参照してください。発生する可能性のある HTTP エラー・コードの完全なリストについては、RESTful API HTTP エラーメッセージを参照してください。
RESTful API コマンド・ターゲットと特性
表 1 は、POST メソッドでの /auth を含むすべてのコマンドについて示しています。また、ユーザーが実行する他のコマンドすべてを認証するためには、/auth コマンド・ターゲットが返した認証トークンを使用する必要があることも示します。/auth コマンド・ターゲットを除き、システム IP アドレスに対してコマンドを実行することで、それらのコマンドが構成ノードによって実行されるようになります。
| コマンド・ターゲット | メソッド | 認証が必要 | 構成ノード/クラスターで実行 |
|---|---|---|---|
| /auth | POST | いいえ | いいえ |
| その他のすべてのコマンド・ターゲット | POST | Yes | Yes |
コマンド・ターゲットとそれぞれのパラメーターの説明、および使用頻度の低いその他のコマンドの説明は、製品資料の CLI コマンドのセクションに記載されています。
| コマンド・ターゲット | ||
|---|---|---|
| /addhostclustermember | /addhostiogrp | /addhostport |
| /addvdiskaccess | /addvdiskcopy | /addvolumecopy |
| /auth | /chhost | /chnode |
| /chnodecanister | /chrcconsistgrp | /chrcrelationship |
| /chvdisk | /expandvdisksize | /lscurrentuser |
| /lseventlog | /lsfcconsistgrp | /lsfcmap |
| /lsfcmapcandidate | /lsfcmapdependentmaps | /lsfcmapprogress |
| /lshost | /lshostcluster | /lshostclustermember |
| /lshostclustervolumemap | /lshostiogrp | /lshostvdiskmap |
| /lsiogrp | /lsiogrphost | /lsmdiskgrp |
| /lsnode | /lsnodecanister | /lsnodehw |
| /lsnodecanisterhw | /lsnodecanisterstats | /lsnodecanistervpd |
| /lsnodehw | /lsnodestats | /lsnodevpd |
| /lspartnership | /lsrcrelationshipprogress | /lssystem |
| /lssystemip | /lssystemstats | /lsvdisk |
| /lsvdiskaccess | /lsvdiskcopy | /lsvdiskfcmapcopies |
| /lsvdiskfcmappings | /lsvdiskhostmap | /lsvdisksyncprogress |
| /mkfcconsistgrp | /mkfcmap | /mkfcpartnership |
| /mkhost | /mkhostcluster | /mkrcconsistgrp |
| /mkrcrelationship | /mkvdisk | /mkvdiskhostmap |
| /mkvolume | /mkvolumehostclustermap | /movevdisk |
| /prestartfcconsistgrp | /prestartfcmap | /rmfcmap |
| /rmhost | /rmhostcluster | /rmhostclustermember |
| /rmhostiogrp | /rmhostport | /rmvdisk |
| /rmvdiskaccess | /rmvdiskcopy | /rmvdiskhostmap |
| /rmvolume | /rmvolumecopy | /rmvolumehostclustermap |
| /startfcmap | /startrcconsistgrp | /startrcrelationship |
| /stopfcconsistgrp | /stopfcmap | /stoprcconsistgrp |
| /stoprcrelationship | ||
認証の概要
データ暗号化とは別に、HTTPS サーバーでは、API セッションごとに有効なユーザー名とパスワードの認証が必要です。2 つの認証ヘッダー・フィールドを使用して、資格情報 X-Auth-Username および X-Auth-Password を指定してください。
初期認証では、ユーザー名とパスワードを使用して認証ターゲット (/auth) を POST する必要があります。RESTful API サーバーは 16 進数トークンを返します。1 つのセッションは、最大 2 時間のアクティブ状態または 30 分の非アクティブ状態 (どちらか先に発生した方) の間、続きます。非アクティブ状態であるためにセッションが終了するか、割り当てられた最大時間に達した場合は、エラー・コード 403 によって権限が失われたことが示されます。/auth コマンド・ターゲットを使用して、そのユーザー名とパスワードで再認証してください。
https://192.168.10.109:7443/rest/auth, method="POST",
headers={'X-Auth-Username': 'superuser', 'X-Auth-Password': 'passw0rd'}API サーバーに送信された HTTP 要求は、次のようになります。POST /auth HTTPS/HTTPS_version
Host: https://192.168.10.109:7443
Content-type: application/json; charset=UTF-8
Content-length: message_size
X-Auth-Token: 58cfd6acb1676e1cba78b7cb5a9a081d11d1d1cfeb0078083ef225d9c59bf4df
{"attribute": "value","attribute": "value"}
ここで、- 最初の行は、POST メソッド、API ターゲット、プロトコル (HTTPS)、およびプロトコル・バージョン (1.1) が指定された要求行です。
- 2 番目の行は、HTTP 要求をシステム上の正しいポート (7443) および IP アドレスに送信するホスト・ヘッダーです。
- 3 番目の行は、コンテンツ・タイプ (application/json; charset=UTF-8) を指定するコンテンツ・タイプ・ヘッダーです。
- 4 番目の行は、メッセージ・サイズが指定されたコンテンツ長ヘッダーです。
- 5 番目の行は、認証トークンが指定された認証トークン・ヘッダーです。
- 要求のヘッダーと本文の間にはスペースが残っています。パラメーターはいずれも、7 番目の行の JSON 内に示されます。
{
"token": "58cfd6acb1676e1cba78b7cb5a9a081d11d1d1cfeb0078083ef225d9c59bf4df"
}コマンド・ターゲット・パラメーター
始めに
以下の Python 3 の例は、システムとの対話とコマンドの実行を開始するための初期セットアップの実行方法を示しています。他の言語での例については、Perl での RESTful API の使用例およびcurl での使用例を参照してください。
import ssl
import json
import pprint
import urllib.request
import urllib.error
import urllib.parse
no_verify = ssl.create_default_context()
no_verify.check_hostname = False
no_verify.verify_mode = ssl.CERT_NONE
if getattr(ssl, '_https_verify_certificates', None):
ssl._https_verify_certificates(False)
class HostString(str):
"""
Comment: Special subclass of string, for storing arbitrary host-related
attributes (such as auth tokens) without losing any string behavior
"""
def __new__(cls, *args, **kwds):
return super(HostString, cls).__new__(cls, *args, **kwds)
class RESTUtil(object):
show_default=False
default_headers = {}
port = 80
def __init__(self, show=None, catch=True):
self.hosts = {}
self.curr_host = None
self.catch=catch
self.show_default=show if show != None else self.show_default
@property
def host(self):
return self.curr_host
@host.setter
def host(self, hostname):
"""
Comment: Retrieve the HostString object of a known host from its
host name or string definition. Even if the host definition
is provided, we still need to key into self.hosts in case the
client classes are storing things on their HostString objects.
"""
try:
if hostname in self.hosts:
self.curr_host = self.hosts[hostname]
else:
self.curr_host = [h for h in self.hosts.values() if h == hostname][0]
return self.curr_host
except IndexError:
raise KeyError("Unrecognized host/name %s" % hostname)
def add_host(self, hostdef, hostname=None):
hostname = hostname if hostname is None else hostdef
self.hosts[hostname] = HostString(hostdef)
if self.curr_host == None:
self.curr_host = self.hosts[hostname]
return hostname
def command(self, protocol, postfix, method='POST', headers=None, show=None, **cmd_kwds):
"""
Comment: A fairly generic RESTful API request builder.
See subclasses for examples of use.
"""
if show == None:
show = self.show_default
headers = {} if headers == None else headers
url = '%s://%s:%s/%s' % (
protocol,
self.curr_host,
self.port,
postfix
)
request = urllib.request.Request(
url,
headers =dict(self.default_headers, **headers),
data=bytes(json.dumps(cmd_kwds), encoding="utf-8") if cmd_kwds else None)
request.get_method = lambda: method
if show:
self.request_pprint(request)
try:
cmd_out = urllib.request.urlopen(request, context=no_verify).read().decode('utf-8')
except urllib.error.HTTPError as e:
self.exception_pprint(e)
if not self.catch:
raise Exception("RESTful API command failed.")
return
try:
cmd_out = json.loads(cmd_out)
except ValueError:
pass
if show:
print("¥nCommand Output:")
pprint.pprint(cmd_out)
print("")
return cmd_out
@staticmethod
def request_pprint(request):
"""
Comment: Request info print function
(for self.command with show=True)
"""
print(request.get_method(), request.get_full_url(), 'HTTP/1.1')
print('Host:', request.host)
for key, value in request.headers.items():
print(key.upper() + ':', str(value))
if request.data != None:
print()
pprint.pprint(request.data)
@staticmethod
def exception_pprint(http_error):
"""
Comment: HTTPError info print function
"""
print(http_error.code, '--', http_error.reason)
print(http_error.fp.read())
print("")
class SVCREST(RESTUtil):
"""
Comment: RESTful wrapper for the SVC CLI
"""
def __init__(self, host, *args, **kwds):
self.debug = kwds.pop('debug', False)
super().__init__(*args, **kwds)
self.add_host(host)
@property
def default_headers(self):
return {'X-Auth-Token': getattr(self.curr_host, 'token', 'badtoken'),
'Content-Type': 'application/json'}
@property
def port(self):
return getattr(self, '_port', None) or ('7665' if self.debug else '7443')
@property
def protocol(self):
return getattr(self, '_protocol', None) or ('http' if self.debug else 'https')
def command(self, cmd, *args, method="POST", headers=None, show=None, **cmd_kwds):
postfix = '/'.join(
['rest'] + [cmd] + [urllib.parse.quote(str(a)) for a in args]
)
return super().command(
self.protocol,
postfix,
method=method,
headers=headers,
show=show,
**cmd_kwds
)
def authenticate(self, username='superuser', password='passw0rd', show=None):
cmd_out = self.command(
'auth', show=show, method="POST", headers={'X-Auth-Username': username, 'X-Auth-Password': password}
)
if cmd_out:
self.curr_host.token = cmd_out['token']
"""
Comment: First, set your cluster ipaddress.
It's assumed superuser/passw0rd (6 lines above) is the crednetial.
After the authenticate call, you can issue any command in
s.command('') that is an svcinfo or svctask cmmand)
"""
s = SVCREST('192.168.10.109')
s.authenticate()
print(s.command('lssystem'))